#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
稳定性压力测试
测试高并发、资源受限环境、极限用例等场景
"""

import unittest
import threading
import time
import tempfile
import os
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
import random
import string
from unittest.mock import patch, MagicMock

# 添加src目录到Python路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))

from src.main import MigrationTool
from src.parser import MySQLParser
from src.converter import MySQLToKWDBConverter
from src.stability import StabilityManager, CircuitBreaker, RetryHandler
from src.performance import PerformanceProfiler


class TestHighConcurrency(unittest.TestCase):
    """高并发测试"""
    
    def setUp(self):
        self.migration_tool = MigrationTool()
        self.stability_manager = StabilityManager()
        self.temp_dir = tempfile.mkdtemp()
        
    def tearDown(self):
        # 清理临时文件
        import shutil
        shutil.rmtree(self.temp_dir, ignore_errors=True)
        
    def test_concurrent_parsing(self):
        """测试并发解析"""
        parser = MySQLParser()
        
        # 创建测试SQL
        test_sqls = [
            f"CREATE TABLE test_{i} (id INT PRIMARY KEY, name VARCHAR(100));"
            for i in range(50)
        ]
        
        results = []
        errors = []
        
        def parse_sql(sql):
            try:
                result = parser.parse_content(sql)
                results.append(result)
                return True
            except Exception as e:
                errors.append(str(e))
                return False
        
        # 并发执行
        with ThreadPoolExecutor(max_workers=10) as executor:
            futures = [executor.submit(parse_sql, sql) for sql in test_sqls]
            completed = sum(1 for future in as_completed(futures) if future.result())
        
        # 验证结果
        self.assertEqual(completed, 50, f"应该成功解析50个SQL，实际成功{completed}个")
        self.assertEqual(len(errors), 0, f"不应该有错误，但发现: {errors}")
        
    def test_concurrent_conversion(self):
        """测试并发转换"""
        converter = MySQLToKWDBConverter()
        
        # 创建测试语句
        from src.parser import ParsedStatement, StatementType
        
        test_statements = []
        for i in range(30):
            stmt = ParsedStatement(
                statement_type=StatementType.CREATE_TABLE,
                original_sql=f"CREATE TABLE test_{i} (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) DEFAULT CHARSET=utf8);",
                parsed_sql=None,
                table_name=f"test_{i}"
            )
            test_statements.append(stmt)
        
        results = []
        errors = []
        
        def convert_statement(stmt):
            try:
                result = converter.convert_statement(stmt)
                results.append(result)
                return True
            except Exception as e:
                errors.append(str(e))
                return False
        
        # 并发执行
        with ThreadPoolExecutor(max_workers=8) as executor:
            futures = [executor.submit(convert_statement, stmt) for stmt in test_statements]
            completed = sum(1 for future in as_completed(futures) if future.result())
        
        # 验证结果
        self.assertEqual(completed, 30, f"应该成功转换30个语句，实际成功{completed}个")
        self.assertEqual(len(errors), 0, f"不应该有错误，但发现: {errors}")
        
    def test_concurrent_file_processing(self):
        """测试并发文件处理"""
        # 创建多个测试文件
        test_files = []
        for i in range(5):
            test_file = os.path.join(self.temp_dir, f"test_{i}.sql")
            with open(test_file, 'w', encoding='utf-8') as f:
                f.write(f"""
CREATE DATABASE test_db_{i};
USE test_db_{i};
CREATE TABLE users_{i} (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(255) UNIQUE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO users_{i} (name, email) VALUES ('User{i}', 'user{i}@test.com');
""")
            test_files.append(test_file)
        
        results = []
        errors = []
        
        def process_file(input_file):
            try:
                output_file = input_file.replace('.sql', '_kwdb.sql')
                success = self.migration_tool.migrate(
                    input_file=input_file,
                    output_file=output_file,
                    validate=True
                )
                results.append(success)
                return success
            except Exception as e:
                errors.append(str(e))
                return False
        
        # 并发处理文件
        with ThreadPoolExecutor(max_workers=3) as executor:
            futures = [executor.submit(process_file, file) for file in test_files]
            completed = sum(1 for future in as_completed(futures) if future.result())
        
        # 验证结果
        self.assertGreaterEqual(completed, 4, f"应该至少成功处理4个文件，实际成功{completed}个")
        self.assertLessEqual(len(errors), 1, f"错误数量应该很少，但发现{len(errors)}个: {errors}")


class TestResourceConstraints(unittest.TestCase):
    """资源受限环境测试"""
    
    def setUp(self):
        self.migration_tool = MigrationTool()
        self.temp_dir = tempfile.mkdtemp()
        
    def tearDown(self):
        import shutil
        shutil.rmtree(self.temp_dir, ignore_errors=True)
        
    def test_large_file_processing(self):
        """测试大文件处理"""
        # 创建大文件（模拟）
        large_file = os.path.join(self.temp_dir, "large_test.sql")
        
        with open(large_file, 'w', encoding='utf-8') as f:
            f.write("CREATE DATABASE large_test;\nUSE large_test;\n")
            
            # 写入大量CREATE TABLE语句
            for i in range(100):
                f.write(f"""
CREATE TABLE table_{i:03d} (
    id INT AUTO_INCREMENT PRIMARY KEY,
    data_field VARCHAR(255) DEFAULT 'test_data_{i}'
""" + "".join([f",\n    col_{j} INT DEFAULT {j}" for j in range(10)]) + f"""
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
""")
            
            # 写入大量INSERT语句
            for i in range(50):
                f.write(f"INSERT INTO table_{i:03d} (")
                f.write(", ".join([f"col_{j}" for j in range(10)]))
                f.write(") VALUES ")
                values = []
                for k in range(20):
                    value_list = ", ".join([str(random.randint(1, 1000)) for _ in range(10)])
                    values.append(f"({value_list})")
                f.write(", ".join(values) + ";\n")
        
        # 处理大文件
        output_file = os.path.join(self.temp_dir, "large_test_kwdb.sql")
        
        start_time = time.time()
        success = self.migration_tool.migrate(
            input_file=large_file,
            output_file=output_file,
            validate=True
        )
        process_time = time.time() - start_time
        
        # 验证结果
        self.assertTrue(success, "大文件处理应该成功")
        self.assertTrue(os.path.exists(output_file), "输出文件应该存在")
        self.assertLess(process_time, 60, f"处理时间应该在60秒内，实际用时{process_time:.2f}秒")
        
        # 验证输出文件大小合理
        input_size = os.path.getsize(large_file)
        output_size = os.path.getsize(output_file)
        self.assertGreater(output_size, input_size * 0.5, "输出文件大小应该合理")
        
    @patch('psutil.virtual_memory')
    def test_low_memory_handling(self, mock_memory):
        """测试低内存环境处理"""
        # 模拟低内存环境
        mock_memory.return_value = MagicMock(
            total=1024 * 1024 * 1024,  # 1GB
            available=100 * 1024 * 1024,  # 100MB可用
            used=924 * 1024 * 1024  # 924MB已使用
        )
        
        # 创建测试文件
        test_file = os.path.join(self.temp_dir, "memory_test.sql")
        with open(test_file, 'w', encoding='utf-8') as f:
            f.write("""
CREATE DATABASE memory_test;
USE memory_test;
CREATE TABLE test_table (
    id INT AUTO_INCREMENT PRIMARY KEY,
    data TEXT
) ENGINE=InnoDB;
""")
            # 添加一些数据
            for i in range(50):
                f.write(f"INSERT INTO test_table (data) VALUES ('{'x' * 100}_{i}');\n")
        
        # 在低内存环境下处理
        output_file = os.path.join(self.temp_dir, "memory_test_kwdb.sql")
        
        try:
            success = self.migration_tool.migrate(
                input_file=test_file,
                output_file=output_file,
                validate=True
            )
            # 在低内存环境下，系统应该能够优雅处理
            self.assertTrue(success or os.path.exists(output_file), 
                          "低内存环境下应该能够处理或至少生成部分输出")
        except MemoryError:
            self.fail("不应该抛出MemoryError，应该优雅处理内存不足")


class TestExtremeEdgeCases(unittest.TestCase):
    """极限边界用例测试"""
    
    def setUp(self):
        self.migration_tool = MigrationTool()
        self.temp_dir = tempfile.mkdtemp()
        
    def tearDown(self):
        import shutil
        shutil.rmtree(self.temp_dir, ignore_errors=True)
        
    def test_malformed_sql_handling(self):
        """测试畸形SQL处理"""
        malformed_sqls = [
            "CREATE TABLE incomplete",  # 不完整的语句
            "INSERT INTO nonexistent VALUES (1, 2, 3);",  # 引用不存在的表
            "CREATE TABLE `weird``name` (id INT);",  # 奇怪的表名
            "SELECT * FROM users WHERE id = 'injection'; DROP TABLE users; --",  # SQL注入尝试
            "CREATE TABLE test (id INT, id INT);",  # 重复列名
            "",  # 空语句
            "   \n\t   ",  # 只有空白字符
            "CREATE TABLE test (id INT) ENGINE=NonExistentEngine;",  # 不存在的引擎
        ]
        
        for i, sql in enumerate(malformed_sqls):
            test_file = os.path.join(self.temp_dir, f"malformed_{i}.sql")
            with open(test_file, 'w', encoding='utf-8') as f:
                f.write(sql)
            
            output_file = os.path.join(self.temp_dir, f"malformed_{i}_kwdb.sql")
            
            # 系统应该能够处理畸形SQL而不崩溃
            try:
                result = self.migration_tool.migrate(
                    input_file=test_file,
                    output_file=output_file,
                    validate=False  # 不验证，允许部分失败
                )
                # 不管成功与否，都不应该崩溃
                self.assertIsInstance(result, bool, f"处理畸形SQL {i} 时应该返回布尔值")
            except Exception as e:
                # 如果抛出异常，应该是可控的异常
                self.assertNotIsInstance(e, (SystemExit, KeyboardInterrupt), 
                                       f"处理畸形SQL {i} 时不应该抛出系统级异常: {e}")
                
    def test_unicode_and_encoding_issues(self):
        """测试Unicode和编码问题"""
        unicode_test_cases = [
            "CREATE TABLE 测试表 (编号 INT, 姓名 VARCHAR(100));",  # 中文
            "CREATE TABLE тест (ид INT, имя VARCHAR(100));",  # 俄文
            "CREATE TABLE テスト (番号 INT, 名前 VARCHAR(100));",  # 日文
            "CREATE TABLE test (data VARCHAR(100) DEFAULT '🚀🎉✨');",  # Emoji
            "INSERT INTO test VALUES ('café', 'naïve', 'résumé');",  # 重音符号
        ]
        
        for i, sql in enumerate(unicode_test_cases):
            test_file = os.path.join(self.temp_dir, f"unicode_{i}.sql")
            
            # 测试不同编码
            encodings = ['utf-8', 'utf-16', 'latin-1']
            for encoding in encodings:
                try:
                    with open(test_file, 'w', encoding=encoding) as f:
                        f.write(sql)
                    
                    output_file = os.path.join(self.temp_dir, f"unicode_{i}_{encoding}_kwdb.sql")
                    
                    # 尝试处理
                    result = self.migration_tool.migrate(
                        input_file=test_file,
                        output_file=output_file,
                        validate=False
                    )
                    
                    # 验证输出文件存在且非空
                    if result and os.path.exists(output_file):
                        self.assertGreater(os.path.getsize(output_file), 0, 
                                         f"Unicode测试 {i} ({encoding}) 输出文件不应该为空")
                        
                except UnicodeEncodeError:
                    # 某些编码可能无法处理特定字符，这是可以接受的
                    continue
                except Exception as e:
                    self.fail(f"Unicode测试 {i} ({encoding}) 失败: {e}")
                    
    def test_circuit_breaker_under_stress(self):
        """测试熔断器在压力下的表现"""
        circuit_breaker = CircuitBreaker(failure_threshold=3, recovery_timeout=1.0)
        
        # 模拟失败的操作
        def failing_operation():
            raise Exception("模拟失败")
        
        # 模拟成功的操作
        def success_operation():
            return "success"
        
        # 测试熔断器开启
        failure_count = 0
        for i in range(10):
            try:
                circuit_breaker.call(failing_operation)
            except Exception:
                failure_count += 1
        
        # 应该有一些失败，但不是全部（因为熔断器会开启）
        self.assertGreaterEqual(failure_count, 3, "应该有至少3次失败触发熔断器")
        self.assertLessEqual(failure_count, 6, "熔断器开启后应该减少失败次数")
        
        # 等待恢复
        time.sleep(1.1)
        
        # 测试恢复后的成功操作
        try:
            result = circuit_breaker.call(success_operation)
            self.assertEqual(result, "success", "熔断器恢复后应该能够成功执行")
        except Exception as e:
            self.fail(f"熔断器恢复后应该能够成功执行，但失败: {e}")


class TestSystemStability(unittest.TestCase):
    """系统稳定性测试"""
    
    def test_retry_mechanism_effectiveness(self):
        """测试重试机制有效性"""
        retry_handler = RetryHandler(max_retries=3, backoff_factor=1.5)
        
        # 模拟间歇性失败的操作
        self.attempt_count = 0
        
        def intermittent_failure():
            self.attempt_count += 1
            if self.attempt_count < 3:
                raise Exception(f"失败尝试 {self.attempt_count}")
            return f"成功于第 {self.attempt_count} 次尝试"
        
        # 测试重试机制
        start_time = time.time()
        result = retry_handler.retry(intermittent_failure)
        end_time = time.time()
        
        # 验证结果
        self.assertEqual(self.attempt_count, 3, "应该尝试3次")
        self.assertIn("成功于第 3 次尝试", result, "应该在第3次尝试时成功")
        self.assertGreaterEqual(end_time - start_time, 0.2, "应该有重试延迟")
        
    def test_graceful_degradation(self):
        """测试优雅降级"""
        # 模拟部分功能不可用的情况
        migration_tool = MigrationTool()
        
        # 创建测试文件
        test_file = os.path.join(tempfile.gettempdir(), "degradation_test.sql")
        with open(test_file, 'w', encoding='utf-8') as f:
            f.write("""
CREATE DATABASE test_db;
CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(100));
INSERT INTO users VALUES (1, 'Test User');
""")
        
        output_file = os.path.join(tempfile.gettempdir(), "degradation_test_kwdb.sql")
        
        # 在验证功能"不可用"的情况下测试
        with patch.object(migration_tool.validator, 'validate_migration', 
                         side_effect=Exception("验证服务不可用")):
            
            # 系统应该能够继续工作，只是跳过验证
            success = migration_tool.migrate(
                input_file=test_file,
                output_file=output_file,
                validate=True  # 尝试验证，但应该优雅处理失败
            )
            
            # 应该成功完成迁移，即使验证失败
            self.assertTrue(success, "即使验证失败，迁移也应该成功")
            self.assertTrue(os.path.exists(output_file), "输出文件应该存在")
        
        # 清理
        os.unlink(test_file)
        os.unlink(output_file)


if __name__ == '__main__':
    # 运行稳定性测试
    unittest.main(verbosity=2)
